package svc

import (
	"cloud-entropy-storage-system/common/pb/pbfiles"
	"cloud-entropy-storage-system/common/pkg/check"
	"cloud-entropy-storage-system/common/pkg/crc"
	"cloud-entropy-storage-system/common/pkg/flate"
	"cloud-entropy-storage-system/common/pkg/pagin"
	"cloud-entropy-storage-system/common/pkg/read"
	"cloud-entropy-storage-system/common/pkg/uuid"
	"cloud-entropy-storage-system/server/internal/core/server"
	"cloud-entropy-storage-system/server/internal/types/response"
	"cloud-entropy-storage-system/server/model"
	"cloud-entropy-storage-system/server/model/metadata"
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"mime/multipart"
	"path"
	"strings"
	"sync"
	"time"

	"github.com/cilidm/toolbox/file"
	"go.uber.org/zap"
)

type FileSvc struct {
	log     *zap.Logger
	meta    *metadata.MetaData
	manager *server.Manager
	lock    sync.Mutex
}

func NewFileSvc(log *zap.Logger, meta *metadata.MetaData, manager *server.Manager) *FileSvc {
	return &FileSvc{log: log, meta: meta, manager: manager}
}

func (f *FileSvc) GetFile(dst string) ([]byte, string, int, error) {
	f.lock.Lock()
	defer f.lock.Unlock()
	if !f.meta.Has(dst) {
		return nil, "", 0, errors.New("文件不存在")
	}
	det, err := f.meta.Get(dst)
	if err != nil {
		return nil, "", 0, err
	}
	if det == nil {
		return nil, "", 0, errors.New("文件不存在")
	}
	if det.Flag == model.FlagDelete {
		return nil, "", 0, errors.New("文件不存在或已删除")
	}
	vol := f.manager.GetVolume(det.Vid)
	if vol != nil && vol.StorageStatus.IsAlive() {
		client := pbfiles.NewVolumeFileClient(vol.StorageStatus.RpcConn)
		resp, err := client.GetFile(context.Background(), &pbfiles.FileRequest{Fid: det.Fid, Vid: det.Vid})
		if err != nil {
			return nil, "", 0, err
		}
		unData, err := flate.UncompressedByte(resp.Data)
		if err != nil {
			unData = resp.Data
		}
		return unData, det.FileName, int(det.Size), nil
	}
	return nil, "", 0, errors.New("该存储节点暂未启动")
}

func (f *FileSvc) DeleteFile(dst string) error {
	if strings.HasPrefix(dst, "//") {
		dst = dst[1:]
	}
	if !f.meta.Has(dst) {
		return errors.New("文件不存在")
	}
	det, err := f.meta.Get(dst)
	if err != nil {
		f.log.Error(err.Error())
		return err
	}
	if det == nil || det.Flag == model.FlagDelete {
		return errors.New("文件不存在")
	}
	vol := f.manager.GetVolume(det.Vid)
	if vol != nil && vol.StorageStatus.IsAlive() {
		client := pbfiles.NewVolumeFileClient(vol.StorageStatus.RpcConn)
		_, err = client.DeleteFile(context.Background(), &pbfiles.DeleteRequest{Fid: det.Fid, Vid: det.Vid})
		if err != nil {
			f.log.Error(err.Error())
			return err
		}
		err = f.meta.Delete(det.ID)
		if err != nil {
			f.log.Error(err.Error())
			return err
		}
		return nil
	}
	return errors.New("服务器离线，请稍后再试")
}

func (f *FileSvc) Count() int {
	num, err := f.meta.Count()
	if err != nil {
		f.log.Error("meta.Count", zap.Error(err))
		return 0
	}
	return num
}

func (f *FileSvc) List(page, limit int, host string) (info []response.MetaRender, err error) {
	data, err := f.meta.List(page, limit)
	if err != nil {
		return nil, err
	}
	for _, meta := range data {
		info = append(info, response.NewMetaRender(meta, host))
	}
	return info, nil
}

func (f *FileSvc) Upload(md5 string, data []byte, crcCode uint32, header *multipart.FileHeader, dst string) error {
	if f.meta.Has(dst) {
		return errors.New("此地址已存在")
	}

	// md5, data := read.ByMultipart(fi, int(header.Size))
	// crcCode := crc.GetCrc32(data)
	// 开启秒传
	if f.meta.HasMD(md5) {
		mdDetail, err := f.meta.GetMD(md5)
		if err != nil {
			return err
		}
		if mdDetail.FilePath == dst {
			return errors.New("此地址已存在")
		}
		if mdDetail.Flag == model.FlagInit { // 如果md存在，并且状态正常，进行秒传
			return f.meta.Set(&metadata.MetaDetail{
				FilePath:  dst,
				Md:        mdDetail.Md,
				Vid:       mdDetail.Vid,
				Fid:       mdDetail.Fid,
				FileName:  header.Filename,
				Crc:       mdDetail.Crc,
				Size:      uint64(header.Size),
				Flag:      model.FlagInit,
				IsImg:     check.IsImg(header.Filename),
				CreatedAt: time.Now(),
			})
		}
	}

	rpcClient := f.manager.GetStorageConn()
	if rpcClient == nil {
		return errors.New("暂无可用storage连接")
	}

	fid := uuid.GenerateUUID()
	comData, err := flate.Compress(data) // 压缩
	if err != nil {
		comData = data
	}
	resp, err := rpcClient.Upload(&pbfiles.UploadRequest{
		Data: comData,
		Size: uint64(header.Size),
		Name: header.Filename,
		Crc:  crcCode,
		MD:   md5,
		Path: dst,
		Fid:  fid,
	})
	if err != nil {
		return err
	}
	return f.meta.Set(&metadata.MetaDetail{
		FilePath:  dst,
		Md:        md5,
		Vid:       resp.Vid,
		Fid:       fid,
		FileName:  header.Filename,
		Crc:       uint64(crcCode),
		Flag:      model.FlagInit,
		IsImg:     check.IsImg(header.Filename),
		Size:      uint64(header.Size),
		CreatedAt: time.Now(),
	})
}

func CheckFileName(dst, fName string) string {
	if dst == "/" {
		dst = fName
	} else if !strings.HasSuffix(dst, fName) {
		dst = path.Join(dst, fName)
	}
	return dst
}

func (f *FileSvc) Gallery(page, limit int, host, dir string) (*response.FileImgRender, error) {
	fileImg, err := f.meta.ImgList(page, limit, dir)
	if err != nil {
		f.log.Error("f.meta.ImgList", zap.Error(err))
		return nil, err
	}
	var imgStr []string
	for _, v := range fileImg {
		imgStr = append(imgStr, host+path.Join("/api/file", v.FilePath))
	}
	imgCount, _ := f.meta.ImgCount()
	return response.NewFileImgRender(imgStr, pagin.NewPaginator(page, 10, imgCount).Do().GetTotal(), page), nil
}

func (f *FileSvc) UploadV2(fi multipart.File, header *multipart.FileHeader, dst string) error {
	if f.meta.Has(dst) {
		return errors.New("此地址已存在")
	}

	md5, data := read.ByMultipart(fi, int(header.Size))
	crcCode := crc.GetCrc32(data)
	// 开启秒传
	if f.meta.HasMD(md5) {
		mdDetail, err := f.meta.GetMD(md5)
		if err != nil {
			return err
		}
		if mdDetail.FilePath == dst {
			return errors.New("此地址已存在")
		}
		if mdDetail.Flag == model.FlagInit { // 如果md存在，并且状态正常，进行秒传
			return f.meta.Set(&metadata.MetaDetail{
				FilePath:  dst,
				Md:        mdDetail.Md,
				Vid:       mdDetail.Vid,
				Fid:       mdDetail.Fid,
				FileName:  header.Filename,
				Crc:       mdDetail.Crc,
				Size:      uint64(header.Size),
				Flag:      model.FlagInit,
				IsImg:     check.IsImg(header.Filename),
				CreatedAt: time.Now(),
			})
		}
	}

	fid := uuid.GenerateUUID()
	comData, err := flate.Compress(data) // 压缩
	if err != nil {
		comData = data
	}

	err = f.saveFile(dst, comData)
	if err != nil {
		f.log.Error(err.Error())
		return err
	}

	return f.meta.Set(&metadata.MetaDetail{
		FilePath:  dst,
		Md:        md5,
		Fid:       fid,
		FileName:  header.Filename,
		Crc:       uint64(crcCode),
		Flag:      model.FlagInit,
		IsImg:     check.IsImg(header.Filename),
		Size:      uint64(header.Size),
		CreatedAt: time.Now(),
	})
}

func (f *FileSvc) saveFile(dst string, content []byte) error {
	fmt.Println(dst)
	dir, _ := path.Split(dst)
	fmt.Println(dir)
	err := file.IsNotExistMkDir(dir)
	if err != nil {
		return err
	}
	if err = ioutil.WriteFile(dst, content, 0666); err != nil {
		return err
	}
	return nil
}
